package com.zhouhx.flow.tasklist;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.Entity;
import com.alibaba.fastjson2.JSONObject;
import com.zhouhx.flow.tasklist.entity.FlowConf;
import com.zhouhx.flow.tasklist.entity.FlowInstance;
import com.zhouhx.flow.tasklist.entity.FlowStep;
import com.zhouhx.flow.tasklist.entity.FlowStepTimeOut;
import com.zhouhx.flow.tasklist.enums.FlowInstanceStatusEnum;
import com.zhouhx.flow.tasklist.enums.FlowStepStatusEnum;
import com.zhouhx.flow.tasklist.enums.FlowStepTypeEnum;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author : zhouhx
 * 任务管理类
 * @date : 2024/4/25 上午11:27
 */
@Data
public class TaskManager {
    private final Logger log = LoggerFactory.getLogger("auto_flow");
    /**
     * 流程配置
     */
    private FlowConf flowConf;
    /**
     * 流程实例id
     */
    private FlowInstance flowInstance;
    /**
     * 任务清单
     * key 为步骤id
     */
    private ConcurrentMap<String, FlowStep> taskMap;
    /**
     * 任务id
     */
    private Long taskId;
    /**
     * 当前流程步骤
     */
    private FlowStep currentStep;
    /**
     * 传递参数
     */
    private Object data;

    public TaskManager(FlowConf flowConf, FlowInstance flowInstance, ConcurrentMap<String, FlowStep> taskMap){
        init(null, flowConf, flowInstance, taskMap);
    }

    public TaskManager(Object data,FlowConf flowConf, FlowInstance flowInstance, ConcurrentMap<String, FlowStep> taskMap){
        init(data, flowConf, flowInstance, taskMap);
    }

    private void init(Object data, FlowConf flowConf, FlowInstance flowInstance, ConcurrentMap<String, FlowStep> taskMap) {
        this.flowConf = flowConf;
        this.flowInstance = flowInstance;
        this.taskMap = taskMap;
        this.taskId= IdUtil.getSnowflakeNextId();
        this.data = data;

        Assert.notNull(flowConf, "流程配置不能为空");
        Assert.notNull(flowInstance, "流程实例不能为空");
        Assert.notNull(taskMap, "流程清单不能为空");
        Assert.notNull(flowConf.getEquipmentNo(), "设备编号不能为空");
        checkFlowStep(taskMap);
        flowInstance.check();
        Assert.notNull(getStartFlowStep(), "必须存在开始步骤");
        Assert.notNull(getEndStep(), "必须存在结束步骤");

        String traceId = IdUtil.nanoId(11);
        MDC.put("traceId", traceId);
    }

    /**
     * 检查流程步骤参数
     * @param taskMap 流程步骤
     */
    private static void checkFlowStep(ConcurrentMap<String, FlowStep> taskMap) {
        taskMap.forEach((k, v)->{
            v.check();
        });
    }

    /**
     * 自动执行流程
     */
    public void startTaskExecution() {
        try {
            int currentFlowStatus = flowInstance.getCurrentFlowStatus();
            if(FlowInstanceStatusEnum.FINISH.getStatus() == currentFlowStatus){
                // 流程结束，不在执行
                return;
            }
            if(ObjectUtil.isNotEmpty(currentStep) && currentStep.getStepType().equals(FlowStepTypeEnum.END.getType())){
                // 流程结束，不在执行
                finishStep(currentStep,FlowStepStatusEnum.FINISH);
                finishFlow("流程办结",FlowInstanceStatusEnum.FINISH);
                return;
            }
            FlowStepTimeOut stepTimeOut = flowConf.getFlowStepTimeOut();
            if(ObjectUtil.isEmpty(stepTimeOut)){
                stepTimeOut = new FlowStepTimeOut(5, DateUnit.SECOND);
            }
            // 当前步骤为空，则执行开始流程
            if(ObjectUtil.isEmpty(currentStep)){
                FlowStep flowStep = getStartFlowStep();
                createStaretFlowStep(flowStep);
            }else{
                // 步骤执行次数
                int count = currentStep.getCount();
                // 重试
                boolean retry = flowConf.isRetry();
                // 重试次数
                int retryCount = flowConf.getRetryCount();
                Date createdTime = DateUtil.parse(currentStep.getCreatedTime(), DatePattern.NORM_DATETIME_MS_PATTERN);
                // 现在流程步骤执行时间
                long time = DateUtil.between(createdTime, new Date(), stepTimeOut.getTimeUnit());
                if(time > stepTimeOut.getTimeOut()){
                    if(retry){
                        if(count < retryCount){
                            count++;
                            // 重试
                            log.debug("taskId:{}-----流程任务-重试>>>>>>流程名称：{},设备号:{},第{}次重试,开始执行流程步骤【{}】",this.taskId,this.flowInstance.getFlowName(),flowConf.getEquipmentNo(),count,currentStep.getFlowStepName());
                            currentStep.execute(flowConf.getEquipmentNo());
                            currentStep.setCount(count);
                        }else{
                            String errorMsg = currentStep.getErrorMsg();
                            if(ObjectUtil.isEmpty(errorMsg)){
                                errorMsg = "未获取到正确状态";
                                currentStep.setErrorMsg(errorMsg);
                            }
                            // 超过重试次数，自动办结流程
                            finishStep(currentStep,FlowStepStatusEnum.FAIL);
                            finishFlow(String.format("步骤名称：%s,执行失败(重试次数：%s),原因:%s",currentStep.getFlowStepName(),count,currentStep.getErrorMsg()),FlowInstanceStatusEnum.EXCEPTION);
                        }
                    }else {
                        String errorMsg = currentStep.getErrorMsg();
                        if(ObjectUtil.isEmpty(errorMsg)){
                            errorMsg = "未获取到正确状态";
                            currentStep.setErrorMsg(errorMsg);
                        }
                        // 流程步骤超时，自动办结流程
                        finishStep(currentStep,FlowStepStatusEnum.FAIL);
                        finishFlow(String.format("步骤名称：%s,执行失败(步骤时间超时,超时时间：%s秒),原因:%s",time,currentStep.getFlowStepName(),currentStep.getErrorMsg()),FlowInstanceStatusEnum.EXCEPTION);
                    }
                }else{
                    // 已经有任务执行了，先判断状态，状态对了，则执行下一个
                    extractedNextStep();
                }
            }
        } catch (Exception e) {
            log.error("taskId:{}-----自动执行流程任务异常",this.taskId,e);
        }
    }

    /**
     * 执行下一步流程
     */
    private void extractedNextStep() {
        String state = currentStep.getState(flowConf.getEquipmentNo());
        String excuteFunctionStatus = currentStep.getExcuteFunctionStatus();
        if(ObjectUtil.isNotEmpty(excuteFunctionStatus) && excuteFunctionStatus.equals(state)){
            // 完结步骤
            finishStep(currentStep,FlowStepStatusEnum.FINISH);
            // 执行下一个步骤
            FlowStep flowStep = taskMap.get(currentStep.getNextFlowStepId());
            FlowStep endStep = getEndStep();
            if(currentStep.getFlowStepId().equals(endStep.getFlowStepId())){
                // 没有下一个步骤，说明流程已经在最后一个环节
                // 办结流程
                finishFlow("流程办结",FlowInstanceStatusEnum.FINISH);
            }else{
                // 执行下一步骤
                createFlowStep(flowStep);
            }

        }
    }

    /**
     * 办结流程
     */
    private void finishFlow(String remark,FlowInstanceStatusEnum statusEnum) {
        log.debug("taskId:{}-----流程任务>>>>>>流程名称：{},流程实例id:{},流程结束原因:{}",this.taskId,this.flowInstance.getFlowName(),flowInstance.getFlowInstanceId(),remark);
        Date endTime = new Date();
        String flowCreateTime = flowInstance.getFlowCreateTime();
        if(ObjectUtil.isNotEmpty(flowCreateTime)){
            long time = DateUtil.between(DateUtil.parse(flowCreateTime, DatePattern.NORM_DATETIME_MS_PATTERN), endTime, DateUnit.SECOND);
            flowInstance.setUseTime(String.format("%s秒",time));;
        }
        flowInstance.setCurrentFlowStatus(statusEnum.getStatus());
        flowInstance.setFlowEndTime(DateUtil.format(endTime, DatePattern.NORM_DATETIME_MS_PATTERN));
        flowInstance.setRemark(remark);
        Entity entity = Entity.create();
        entity.set("流程名称",this.flowInstance.getFlowName());
        entity.set("设备号",this.flowConf.getEquipmentNo());
        entity.set("流程实例",flowInstance);
        Collection<FlowStep> values = this.getTaskMap().values();
        // 按照时间步骤id排序
        List<FlowStep> steps = values.stream().sorted(Comparator.comparing(FlowStep::getFlowStepId)).collect(Collectors.toList());
        entity.set("流程步骤",steps);

        log.info("taskId:{}-----流程任务>>>>>> 流程实例:{}",this.taskId, JSONObject.toJSONString(entity));
        Function<TaskManager,?> fn = flowConf.getPosteriorFn();
        if(ObjectUtil.isNotEmpty(fn)){
            fn.apply(this);
        }
        MDC.remove("traceId");
    }
    /**
     * 完结流程步骤
     * errorMessage: 执行失败错误信息
     */
    private void finishStep(FlowStep step,FlowStepStatusEnum statusEnum) {
        log.debug("taskId:{}-----流程任务>>>>>>流程名称：{},流程实例id:{},流程步骤【{}】已结束，原因:{}",this.taskId,this.flowInstance.getFlowName(),flowInstance.getFlowInstanceId(),step.getFlowStepName(),step.getErrorMsg());
        step.setFlowStepStatus(statusEnum.getStatus());
        step.setEndTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN));
        Entity entity = Entity.create();
        entity.set("流程名称",this.flowInstance.getFlowName());
        entity.set("设备号",this.flowConf.getEquipmentNo());
        entity.set("流程步骤",this.getCurrentStep());
        log.debug("taskId:{}-----流程任务>>>>>> 流程步骤:{}",this.taskId, JSONObject.toJSONString(entity));
    }

    /**
     * 创建开始流程步骤
     * @param flowStep 流程步骤
     */
    private void createStaretFlowStep(FlowStep flowStep) {
        createFlowStep(flowStep);
        // 更新流程实例
        flowInstance.setCurrentFlowStatus(FlowInstanceStatusEnum.RUNNING.getStatus());
        flowInstance.setFlowCreateTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN));
        flowInstance.setCurrentFlowStepName(flowStep.getFlowStepName());
    }
    /**
     * 创建流程步骤
     * @param flowStep 流程步骤
     */
    private void createFlowStep(FlowStep flowStep) {
        log.debug("taskId:{}-----流程任务>>>>>>流程名称：{},流程实例id:{},开始执行流程步骤【{}】",this.taskId,this.flowInstance.getFlowName(),flowInstance.getFlowInstanceId(),flowStep.getFlowStepName());
        flowStep.execute(flowConf.getEquipmentNo());
        flowStep.setCount(1);
        flowStep.setCreatedTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN));
        this.currentStep = flowStep;
        // 更新流程实例
        flowInstance.setCurrentFlowStepName(flowStep.getFlowStepName());
    }
    /**
     * 获取开始流程步骤
     * @return
     */
    private FlowStep getStartFlowStep() {
        AtomicReference<FlowStep> step = new AtomicReference<>();
        taskMap.forEach((key,value)->{
            if(value.getStepType().equals(FlowStepTypeEnum.START.getType())){
                step.set(value);
            }
        });

        return step.get();
    }

    /**
     * 获取结束流程步骤
     * @return
     */
    private FlowStep getEndStep() {
        AtomicReference<FlowStep> step = new AtomicReference<>();
        taskMap.forEach((key,value)->{
            if(value.getStepType().equals(FlowStepTypeEnum.END.getType())){
                step.set(value);
            }
        });

        return step.get();
    }
}
